﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace Als.Utils.File
{
    public static class XmlHelper
    {

        #region 读取数据

        /// <summary>
        /// 读取数据
        /// XmlHelper.Read(path, "/Node", "")
        /// XmlHelper.Read(path, "/Node/Element[@Attribute='Name']", "Attribute")
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="node">节点</param>
        /// <param name="attribute">属性名，非空时返回该属性值，否则返回串联值</param>
        /// <returns>string</returns>
        public static string Read(string path, string node, string attribute)
        {
            var value = "";
            try
            {
                var doc = new XmlDocument();
                doc.Load(path);
                var xn = doc.SelectSingleNode(node);
                if (xn?.Attributes != null)
                    value = (attribute.Equals("") ? xn.InnerText : xn.Attributes[attribute].Value);
            }
            catch (Exception e)
            {
                return e.Message;
            }
            return value;
        }

        #endregion

        #region 插入数据

        /// <summary>
        /// 插入数据
        /// XmlHelper.Insert(path, "/Node", "Element", "", "Value")
        /// XmlHelper.Insert(path, "/Node", "Element", "Attribute", "Value")
        /// XmlHelper.Insert(path, "/Node", "", "Attribute", "Value")
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="node">节点</param>
        /// <param name="element">元素名，非空时插入新元素，否则在该元素中插入属性</param>
        /// <param name="attribute">属性名，非空时插入该元素属性值，否则插入元素值</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public static void Insert(string path, string node, string element, string attribute, string value)
        {
            var doc = new XmlDocument();
            doc.Load(path);
            var xn = doc.SelectSingleNode(node);
            if (element.Equals(""))
            {
                if (!attribute.Equals(""))
                {
                    var xe = (XmlElement)xn;
                    xe?.SetAttribute(attribute, value);
                }
            }
            else
            {
                var xe = doc.CreateElement(element);
                if (attribute.Equals(""))
                    xe.InnerText = value;
                else
                    xe.SetAttribute(attribute, value);
                xn?.AppendChild(xe);
            }
            doc.Save(path);
        }

        #endregion

        #region 修改数据

        /// <summary>
        /// 修改数据
        /// XmlHelper.Insert(path, "/Node", "", "Value")
        /// XmlHelper.Insert(path, "/Node", "Attribute", "Value")
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="node">节点</param>
        /// <param name="attribute">属性名，非空时修改该节点属性值，否则修改节点值</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public static void Update(string path, string node, string attribute, string value)
        {
            try
            {
                var doc = new XmlDocument();
                doc.Load(path);
                var xn = doc.SelectSingleNode(node);
                var xe = (XmlElement)xn;
                if (attribute.Equals(""))
                {
                    if (xe != null) xe.InnerText = value;
                }
                else
                {
                    xe?.SetAttribute(attribute, value);
                }
                doc.Save(path);
            }
            catch (Exception)
            {
                return;
            }
        }

        #endregion

        #region 删除数据

        /// <summary>
        /// 删除数据
        /// XmlHelper.Delete(path, "/Node", "")
        /// XmlHelper.Delete(path, "/Node", "Attribute")
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="node">节点</param>
        /// <param name="attribute">属性名，非空时删除该节点属性值，否则删除节点值</param>
        /// <returns></returns>
        public static void Delete(string path, string node, string attribute)
        {
            try
            {
                var doc = new XmlDocument();
                doc.Load(path);
                var xn = doc.SelectSingleNode(node);
                var xe = (XmlElement)xn;
                if (attribute.Equals(""))
                {
                    xn?.ParentNode?.RemoveChild(xn);
                }
                else
                {
                    xe?.RemoveAttribute(attribute);
                }
                doc.Save(path);
            }
            catch
            {
                return;
            }
        }

        #endregion

        #region 扩展

        /// <summary>
        /// 列出全部标签
        /// </summary>
        public static IEnumerable<XElement> AllElements(this XElement element, string name)
        {
            var list = new List<XElement>();
            if (element != null)
            {
                list.AddRange(element.Nodes().Where(node => node.NodeType == XmlNodeType.Element).Select(node => (XElement)node).Where(item => item.Name.LocalName == name));
            }
            return list;
        }
        /// <summary>
        /// 列出全部标签
        /// </summary>
        public static IEnumerable<XElement> AllElements(this IEnumerable<XElement> elements, string name)
        {
            var list = new List<XElement>();
            foreach (var element in elements)
            {
                list.AddRange(element.AllElements(name));
            }
            return list;
        }
        /// <summary>
        /// 列出全部标签
        /// </summary>
        public static XAttribute AnyAttribute(this XElement element, string name)
        {
            return element?.Attributes().FirstOrDefault(attribute => attribute.Name.LocalName == name);
        }
        /// <summary>
        /// 判断标签是否存在
        /// </summary>
        public static XElement AnyElement(this IEnumerable<XElement> elements, string name)
        {
            return elements.FirstOrDefault(element => element.Name.LocalName == name);
        }
        /// <summary>
        /// 判断标签是否存在
        /// </summary>
        public static XElement AnyElement(this XElement element, string name)
        {
            return element != null ? (from node in element.Nodes() where node.NodeType == XmlNodeType.Element select (XElement)node).FirstOrDefault(element2 => element2.Name.LocalName == name) : null;
        }
        /// <summary>
        /// 判断标签是否存在值
        /// </summary>
        public static void AssertElementHasValue(this XElement element, string name)
        {
            if (element == null)
            {
                throw new ArgumentNullException(nameof(element));
            }
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            var element2 = element.AnyElement(name);
            if (string.IsNullOrEmpty(element2?.Value))
            {
                throw new ArgumentNullException(name, $"{name} is required");
            }
        }
        /// <summary>
        /// 获取第一个标签
        /// </summary>
        public static XElement FirstElement(this XElement element)
        {
            if (element.FirstNode.NodeType == XmlNodeType.Element)
            {
                return (XElement)element.FirstNode;
            }
            return null;
        }
        /// <summary>
        /// 获取Bool类型
        /// </summary>
        public static bool GetBool(this XElement el, string name)
        {
            el.AssertElementHasValue(name);
            return (bool)el.GetElement(name);
        }
        /// <summary>
        /// 获取默认Bool类型
        /// </summary>
        public static bool GetBoolOrDefault(this XElement el, string name)
        {
            return el.GetElementValueOrDefault<bool>(name, x => ((bool)x));
        }
        /// <summary>
        /// 获取时间类型
        /// </summary>
        public static DateTime GetDateTime(this XElement el, string name)
        {
            el.AssertElementHasValue(name);
            return (DateTime)el.GetElement(name);
        }
        /// <summary>
        /// 获取默认时间类型
        /// </summary>
        public static DateTime GetDateTimeOrDefault(this XElement el, string name)
        {
            return el.GetElementValueOrDefault<DateTime>(name, x => ((DateTime)x));
        }
        /// <summary>
        /// 获取Decimal类型
        /// </summary>
        public static decimal GetDecimal(this XElement el, string name)
        {
            el.AssertElementHasValue(name);
            return (decimal)el.GetElement(name);
        }
        /// <summary>
        /// 获取默认Decimal类型值
        /// </summary>
        public static decimal GetDecimalOrDefault(this XElement el, string name)
        {
            return el.GetElementValueOrDefault<decimal>(name, x => ((decimal)x));
        }
        /// <summary>
        /// 获取标签
        /// </summary>
        public static XElement GetElement(this XElement element, string name)
        {
            if (element == null)
            {
                throw new ArgumentNullException(nameof(element));
            }
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            return element.AnyElement(name);
        }
        /// <summary>
        /// 获取标签并转为对象
        /// </summary>
        public static T GetElementValueOrDefault<T>(this XElement element, string name, Func<XElement, T> converter)
        {
            if (converter == null)
            {
                throw new ArgumentNullException(nameof(converter));
            }
            var element2 = element.GetElement(name);
            if ((element2 != null) && !string.IsNullOrEmpty(element2.Value))
            {
                return converter(element2);
            }
            return default(T);
        }
        /// <summary>
        /// 获取GUID值
        /// </summary>
        public static Guid GetGuid(this XElement el, string name)
        {
            el.AssertElementHasValue(name);
            return (Guid)el.GetElement(name);
        }
        /// <summary>
        /// 获取默认GUID值
        /// </summary>
        public static Guid GetGuidOrDefault(this XElement el, string name)
        {
            return el.GetElementValueOrDefault<Guid>(name, x => ((Guid)x));
        }
        /// <summary>
        /// 获取Int类型值
        /// </summary>
        public static int GetInt(this XElement el, string name)
        {
            el.AssertElementHasValue(name);
            return (int)el.GetElement(name);
        }
        /// <summary>
        /// 获取默认Int类型值
        /// </summary>
        public static int GetIntOrDefault(this XElement el, string name)
        {
            return el.GetElementValueOrDefault<int>(name, x => ((int)x));
        }
        /// <summary>
        /// 获取Long类型值
        /// </summary>
        public static long GetLong(this XElement el, string name)
        {
            el.AssertElementHasValue(name);
            return (long)el.GetElement(name);
        }
        /// <summary>
        /// 获取默认Long类型值
        /// </summary>
        public static long GetLongOrDefault(this XElement el, string name)
        {
            return el.GetElementValueOrDefault<long>(name, x => ((long)x));
        }
        /// <summary>
        /// 获取可以为空的bool类型值
        /// </summary>
        public static bool? GetNullableBool(this XElement el, string name)
        {
            var element = el.GetElement(name);
            if (!string.IsNullOrEmpty(element?.Value))
            {
                return (bool?)element;
            }
            return null;
        }
        /// <summary>
        /// 获取可以为空的时间类型值
        /// </summary>
        public static DateTime? GetNullableDateTime(this XElement el, string name)
        {
            var element = el.GetElement(name);
            if (!string.IsNullOrEmpty(element?.Value))
            {
                return (DateTime?)element;
            }
            return null;
        }
        /// <summary>
        /// 获取可以为空的decimal类型值
        /// </summary>
        public static decimal? GetNullableDecimal(this XElement el, string name)
        {
            var element = el.GetElement(name);
            if (!string.IsNullOrEmpty(element?.Value))
            {
                return (decimal?)element;
            }
            return null;
        }
        /// <summary>
        /// 获取可以为空的GUID值
        /// </summary>
        public static Guid? GetNullableGuid(this XElement el, string name)
        {
            var element = el.GetElement(name);
            if (!string.IsNullOrEmpty(element?.Value))
            {
                return (Guid?)element;
            }
            return null;
        }
        /// <summary>
        /// 获取可以为空的Int类型值
        /// </summary>
        public static int? GetNullableInt(this XElement el, string name)
        {
            var element = el.GetElement(name);
            if (!string.IsNullOrEmpty(element?.Value))
            {
                return (int?)element;
            }
            return null;
        }
        /// <summary>
        /// 获取可以为空的long类型值
        /// </summary>
        public static long? GetNullableLong(this XElement el, string name)
        {
            var element = el.GetElement(name);
            if (!string.IsNullOrEmpty(element?.Value))
            {
                return (long?)element;
            }
            return null;
        }
        /// <summary>
        /// 获取可以为空的Timespan类型值
        /// </summary>
        public static TimeSpan? GetNullableTimeSpan(this XElement el, string name)
        {
            var element = el.GetElement(name);
            if (!string.IsNullOrEmpty(element?.Value))
            {
                return (TimeSpan?)element;
            }
            return null;
        }
        /// <summary>
        /// 获取字符串类型值
        /// </summary>
        public static string GetString(this XElement el, string name)
        {
            return el?.GetElementValueOrDefault<string>(name, x => x.Value);
        }
        /// <summary>
        /// 获取Timespan类型值
        /// </summary>
        public static TimeSpan GetTimeSpan(this XElement el, string name)
        {
            el.AssertElementHasValue(name);
            return (TimeSpan)el.GetElement(name);
        }
        /// <summary>
        /// 获取默认TimeSpan类型值
        /// </summary>
        public static TimeSpan GetTimeSpanOrDefault(this XElement el, string name)
        {
            return el.GetElementValueOrDefault<TimeSpan>(name, x => ((TimeSpan)x));
        }
        /// <summary>
        /// 获取值列表
        /// </summary>
        public static List<string> GetValues(this IEnumerable<XElement> els)
        {
            return els.Select(element => element.Value).ToList();
        }

        #endregion

    }
}
